#include <assert.h>
#include <fstream>
#include "imageLoader.h"
#include "error.h"
#include "loadingFunctions.h"
#include "gl/gl.h"
#include "math.h"

using namespace std;


Image* loadBMP(const char* filename)
{
	printf("Loading bitmap: %s...\n", filename);

	ifstream input;
	input.open(filename, ifstream::binary);

	//file not found
	if (input.fail())
	{
		Error::printError(IMAGE_NOT_FOUND, filename);
		return NULL;
	}
	char buffer[2];
	input.read(buffer, 2);

	//file is not a bitmap file
	if (!(buffer[0] == 'B' && buffer[1] == 'M'))
	{
		Error::printError(NOT_BITMAP_FILE, filename);
		return NULL;
	}

	input.ignore(8);
	int dataOffset = readInt(input);
	
	//read the header
	int headerSize = readInt(input);
	int width;
	int height;

	char error[100];
	memset(error, '\0', 100);

	switch(headerSize)
	{
		case 40:
			//Windows V3
			width = readInt(input);
			height = readInt(input);
			input.ignore(2);
			assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
			assert(readShort(input) == 0 || !"Image is compressed");
			break;
		case 12:
			//OS/2 V1
			width = readShort(input);
			height = readShort(input);
			input.ignore(2);
			assert(readShort(input) == 24 || !"Image is not 24 bits per pixel");
			break;
		case 64:
			//OS/2 V2
			strcat_s(error, "Can't load OS/2 V2 bitmaps: ");
			strcat_s(error, filename);
			Error::printError(OWN_ERROR, error);
			break;
		case 108:
			//Windows V4
			strcat_s(error, "Can't load Windows V4 bitmaps: ");
			strcat_s(error, filename);
			Error::printError(OWN_ERROR, error);
			break;
		case 124:
			//Windows V5
			strcat_s(error, "Can't load Windows V5 bitmaps: ");
			strcat_s(error, filename);
			Error::printError(OWN_ERROR, error);
			break;
		default:
			strcat_s(error, "Unknown bitmap format: ");
			strcat_s(error, filename);
			Error::printError(OWN_ERROR, error);
	}
	
	//Read the data
	int bytesPerRow = ((width * 3 + 3) / 4) * 4 - (width * 3 % 4);
	int size = bytesPerRow * height;
	auto_array<char> pixels(new char[size]);
	input.seekg(dataOffset, ios_base::beg);
	input.read(pixels.get(), size);
	
	//Get the data into the right format
	auto_array<char> pixels2(new char[width * height * 3]);
	for(int y = 0; y < height; y++)
	{
		for(int x = 0; x < width; x++)
		{
			for(int c = 0; c < 3; c++)
			{
				pixels2[3 * (width * y + x) + c] = pixels[bytesPerRow * y + 3 * x + (2 - c)];
			}
		}
	}
	
	input.close();
	//TODO: fix
	//return new Image(pixels2.release(), width, height);
	return NULL;
}

Image* loadPCX(const char *filename)
{
	Image *image = new Image;

	printf("Loading PCX file: %s...\n", filename);

	ifstream			file;			// file stream
	PCXHEADER			*header;		// header PCX
	unsigned char		*data;			// uncompressed paletted image data
	unsigned char		*ptr;			// pointer to pixels data
	unsigned char		c;				// temporary variable
	char				*buffer;		// buffer storing the entire file
	int					idx = 0;		// temporary variable
	int					numRepeat;		// temporary variable
	int					i, j;			// temporary variables

	unsigned char **pixels = &image->data;
	int *width = &image->width;
	int *height = &image->height;


	file.open(filename, ios::in | ios::binary);

	if(file.fail())
	{
		Error::printError(IMAGE_NOT_FOUND, filename);
		delete image;
		return NULL;
	}

	file.seekg(0, ios::end);
	long flen = file.tellg();
	file.seekg(0, ios::beg);

	buffer = new char[flen + 1];
	file.read(buffer, flen);
	char *pBuff = buffer;

	file.close();

	header = (PCXHEADER *)pBuff;

	if( (header->manufacturer	!= 10)	||
		(header->version		!= 5)	||
		(header->encoding		!= 1)	||
		(header->bitsPerPixel	!= 8) )
	{
		Error::printError(WRONG_FILE_FORMAT, filename);
		delete image;
		return 0;
	}

	header->width	= header->width	 - header->x + 1;
	header->height	= header->height - header->y + 1;

	if(width)
		*width = header->width;

	if(height)
		*height = header->height;

	char error[100];
	memset(error, '\0', 100);

	if(!pixels)
	{
		delete [] buffer;
		strcat_s(error, "No image data in file ");
		strcat_s(error, filename);
		Error::printError(OWN_ERROR, error);
		delete image;
		return NULL;
	}

	// allocate memory for image data
	data = new unsigned char[header->width * header->height];

	pBuff = (char *)&buffer[128];

	// uncode compressed image (RLE)
	while(idx < (header->width * header->height))
	{
		if((c = *(pBuff++)) > 0xbf)
		{
			numRepeat = 0x3f & c;
			c = *(pBuff++);

			for(i = 0; i < numRepeat; i++)
				data[idx++] = c;
		}
		else
			data[idx++] = c;
	}

	// the palette is located at the 769th last byte of the file
	pBuff = &buffer[flen - 769];

	// verify the palette; first char must be equal to 12
	if(*(pBuff++) != 12)
	{
		delete [] buffer;
		delete [] data;
		Error::printError(WRONG_FILE_FORMAT, filename);
		delete image;
		return NULL;
	}

	// read the palette
	header->palette = (unsigned char *)pBuff;

	// allocate memory for 32 bits pixel data
	*pixels = new unsigned char[header->width * header->height * 4];

	ptr = &(*pixels)[0];

	// convert from paletted to 32 bits rgba pixels
	for(j = header->height - 1; j > 0; j--)
	{
		/*if(flipvert)
			ptr = &(*pixels)[ j * header->width * 4];*/

		for(i = 0; i < header->width; i++, ptr += 4)
		{
			int color = 3 * data[j * header->width + i];

			ptr[0] = (unsigned char)header->palette[color + 0];
			ptr[1] = (unsigned char)header->palette[color + 1];
			ptr[2] = (unsigned char)header->palette[color + 2];
			ptr[3] = (unsigned char)255;
		}
	}


	// free memory
	delete [] data;
	delete [] buffer;

	return image;
}

GLuint loadTexture(Image *image)
{
	GLuint textureId;
	glGenTextures(1, &textureId);
	glBindTexture(GL_TEXTURE_2D, textureId);

	glTexImage2D(GL_TEXTURE_2D,
				 0,
				 GL_RGBA,
				 image->width, image->height,
				 0,
				 GL_RGBA,
				 GL_UNSIGNED_BYTE,
				 image->data);

	return textureId;
}